﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Reflection;

namespace AxeFrog.Data
{
	public static class IDbCommandExtensions
	{
		public static T ExecuteScalar<T>(this IDbCommand cmd)
		{
			return (T)Convert.ChangeType(cmd.ExecuteScalar(), typeof(T));
		}

		public static T ExecuteScalar<T>(this IDbCommand cmd, T defaultWhenNoValueExists)
		{
			var val = cmd.ExecuteScalar();
			if(val == null || val == DBNull.Value)
				return defaultWhenNoValueExists;
			return (T)val; // Convert.ChangeType(val, typeof(T));
		}

		public static IDbCommand Execute(this IDbCommand cmd)
		{
			cmd.ExecuteNonQuery();
			return cmd;
		}

		public static IDbCommand SetTransaction(this IDbCommand cmd, IDbTransaction trans)
		{
			cmd.Transaction = trans;
			return cmd;
		}

		public static IDbCommand AddParameter(this IDbCommand cmd, string name, object value, out IDataParameter outprm)
		{
			if(value == null)
				throw new Exception("\"value\" cannot be null unless a DbType is specified");
			IDataParameter prm = cmd.CreateParameter();
			prm.ParameterName = name;
			prm.SetValue(value);
			cmd.Parameters.Add(prm);
			outprm = prm;
			return cmd;
		}

		public static IDbCommand AddParameter(this IDbCommand cmd, string name, object value, DbType dbType, out IDataParameter outprm)
		{
			return AddParameter(cmd, name, value, dbType, ParameterDirection.Input, out outprm);
		}

		public static IDbCommand AddParameter(this IDbCommand cmd, string name, DbType dbType, ParameterDirection direction, out IDataParameter outprm)
		{
			return AddParameter(cmd, name, null, dbType, direction, out outprm);
		}

		public static IDbCommand AddParameter(this IDbCommand cmd, string name, DbType dbType, out IDataParameter outprm)
		{
			return AddParameter(cmd, name, null, dbType, ParameterDirection.Input, out outprm);
		}

		public static IDbCommand AddParameter(this IDbCommand cmd, string name, object value, DbType dbType, ParameterDirection direction, out IDataParameter outprm)
		{
			IDataParameter prm = cmd.CreateParameter();
			prm.ParameterName = name;
			prm.SetValue(value);
			prm.DbType = dbType;
			prm.Direction = direction;
			cmd.Parameters.Add(prm);
			outprm = prm;
			return cmd;
		}

		public static IDbCommand AddParameter(this IDbCommand cmd, string name, object value)
		{
			IDataParameter prm;
			return cmd.AddParameter(name, value, out prm);
		}

		public static IDbCommand AddParameter(this IDbCommand cmd, string name, object value, DbType dbType)
		{
			IDataParameter prm;
			return cmd.AddParameter(name, value, dbType, out prm);
		}

		public static IDbCommand AddParameter(this IDbCommand cmd, string name, DbType dbType, ParameterDirection direction)
		{
			IDataParameter prm;
			return cmd.AddParameter(name, dbType, direction, out prm);
		}

		public static IDbCommand AddParameter(this IDbCommand cmd, string name, object value, DbType dbType, ParameterDirection direction)
		{
			IDataParameter prm;
			return cmd.AddParameter(name, value, dbType, direction, out prm);
		}

		public static IDbCommand If(this IDbCommand cmd, bool condition, Action<IDbCommand> action)
		{
			if(condition)
				action(cmd);
			return cmd;
		}

		public static IDbCommand AddParametersFrom<T>(this IDbCommand cmd, T entity)
		{
			var fields = RepositoryHelper.EnumerateAttributedFields<T>();
			for(int i = 0; i < fields.AllFields.Length; i++)
				cmd.AddParameter("@" + fields.AllFields[i],
								 fields.AllProperties[i].GetValue(entity, null),
								 fields.AllProperties[i].PropertyType.ToDbType());
			return cmd;
		}

		internal static IDbCommand AddNonIdParametersFrom<T>(this IDbCommand cmd, T entity)
		{
			var fields = RepositoryHelper.EnumerateAttributedFields<T>();
			for(int i = 0; i < fields.NonIdFields.Length; i++)
				cmd.AddParameter("@" + fields.NonIdFields[i],
								 fields.NonIdProperties[i].GetValue(entity, null),
								 fields.NonIdProperties[i].PropertyType.ToDbType());
			return cmd;
		}

		internal static IDbCommand AddEmptyParametersFrom<T>(this IDbCommand cmd, out Dictionary<PropertyInfo, IDataParameter> prms)
		{
			prms = new Dictionary<PropertyInfo, IDataParameter>();
			var fields = RepositoryHelper.EnumerateAttributedFields<T>();
			for(int i = 0; i < fields.AllFields.Length; i++)
			{
				IDataParameter prm;
				var property = fields.AllProperties[i];
				cmd.AddParameter("@" + fields.AllFields[i], property.PropertyType.ToDbType(), out prm);
				prms.Add(property, prm);
			}
			return cmd;
		}

		internal static IDbCommand AddEmptyNonIdParametersFrom<T>(this IDbCommand cmd, out Dictionary<PropertyInfo, IDataParameter> prms)
		{
			prms = new Dictionary<PropertyInfo, IDataParameter>();
			var fields = RepositoryHelper.EnumerateAttributedFields<T>();
			for(int i = 0; i < fields.NonIdFields.Length; i++)
			{
				IDataParameter prm;
				var property = fields.NonIdProperties[i];
				cmd.AddParameter("@" + fields.NonIdFields[i], property.PropertyType.ToDbType(), out prm);
				prms.Add(property, prm);
			}
			return cmd;
		}
	}
}